package com.basker.pisces.domain.property.meta;

import java.util.Map;

import org.springframework.util.StringUtils;

import com.basker.pisces.IChild;
import com.basker.pisces.core.Element;
import com.basker.pisces.core.ISupportSort;
import com.basker.pisces.core.meta.IDataField;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.IField;
import com.basker.pisces.core.meta.IReferenceField;
import com.basker.pisces.core.meta.annotation.DataField;
import com.basker.pisces.core.meta.utils.DataObjectMetaUtils;
import com.basker.pisces.domain.entity.data.IDataEntity;
import com.basker.pisces.domain.entity.meta.AbstractEntity;
import com.basker.pisces.domain.entity.meta.EntityHelper;
import com.basker.pisces.domain.property.InvalidPropertyValueException;
import com.basker.pisces.domain.value.IValueObject;
import com.basker.pisces.exception.ExceptionFactory;
import com.basker.pisces.resource.StringResources;

public abstract class Property<TOutputValue extends IValueObject> extends Element
        implements IChild<AbstractEntity>, ISupportSort {

    protected transient IField field;
    private transient AbstractEntity hostEntity;

    @DataField
    private String alias;

    @DataField
    private Boolean required = false;

    @DataField
    private Object initialValue;

    @DataField
    private Boolean enableDefaultDispalyNameRule = false;

    @DataField
    private String invalidInputValueMessagePattern;

    @DataField
    private Integer sort;

    public Property() {

    }

    public void bindField(IDataObjectMeta objectMeta) {
        String propertyName = this.getName();
        IField field = DataObjectMetaUtils.tryFindField(objectMeta, propertyName);

        if (field == null) {
            throw ExceptionFactory.createRuntimeException("pisces-domain.Property.bindField-001",
                    "property:{0} field not found in objectMeta:{1}", propertyName, objectMeta.toString());
        }

        this.field = field;
    }

    public abstract TOutputValue createValue(Object rawValue);

    public void fillMapObject(final IDataEntity dataEntity, final Object dataObject, final Map<String, Object> mapObject) {
        TOutputValue value = this.getValue(dataObject);
        if (IValueObject.isEmpty(value)) {
            return;
        }

        mapObject.put(this.getName(), value.getDataValue());
    }

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    @Override
    public String getDisplayName() {
        if (this.getEnableDefaultDispalyNameRule() && this.hostEntity != null) {
            String key = EntityHelper.getDisplayNameResouceKey(this);
            return StringResources.resolve(key);
        }

        return super.getDisplayName();
    }

    public Boolean getEnableDefaultDispalyNameRule() {
        return enableDefaultDispalyNameRule == null ? false : enableDefaultDispalyNameRule;
    }

    public void setEnableDefaultDispalyNameRule(Boolean enableDefaultDispalyNameRule) {
        this.enableDefaultDispalyNameRule = enableDefaultDispalyNameRule;
    }

    public IField getField() {
        return field;
    }

    public AbstractEntity getHostEntity() {
        return hostEntity;
    }

    public void setHostEntity(AbstractEntity hostEntity) {
        this.hostEntity = hostEntity;
    }

    public Object getInitialValue() {
        return initialValue;
    }

    public void setInitialValue(Object initialValue) {
        this.initialValue = initialValue;
    }

    /**
     * 获取描述非法录入值的消息,支持${xxx}的格式以便从资源文件获取
     *
     * @return
     */
    public String getInvalidInputValueMessagePattern() {
        return invalidInputValueMessagePattern;
    }

    public void setInvalidInputValueMessagePattern(String invalidInputValueMessagePattern) {
        this.invalidInputValueMessagePattern = invalidInputValueMessagePattern;
    }

    public final Object getRawValue(Object dataObject) {
        return this.field.getValue(dataObject);
    }

    public Boolean getRequired() {
        return required == null ? false : required;
    }

    public void setRequired(Boolean required) {
        this.required = required;
    }

    public String getRequiredViolationMessgae(IValueObject propertyValue) {
        return StringResources.get("pisces-domain.Property.RequiredViolation", this.toString());
    }

    public Integer getSort() {
        Integer sort = this.sort;
        if (sort != null) {
            return sort;
        }

        if (this.field instanceof IDataField) {
            sort = ((IDataField) this.field).getSort();
        }

        if (sort == null) {
            sort = 0;
        }

        return sort;
    }

    public void setSort(Integer sort) {
        this.sort = sort;
    }

    public TOutputValue getValue(Object dataObject) {
        Object rawValue = this.getRawValue(dataObject);
        return this.createValue(rawValue);
    }

    public void initial() {

    }

    public void onCopy(Property<?> copyProperty) {

    }

    @Override
    public void setParent(AbstractEntity parent) {
        this.setHostEntity(parent);
    }

    public abstract void setValue(Object dataObject, Object inputValue) throws InvalidPropertyValueException;

    @Override
    public String toString() {
        if (this.hostEntity == null) {
            return "[" + super.toString() + "]";

        } else {
            return "[" + hostEntity.getDisplayName() + "." + super.toString() + "]";
        }
    }

    public IDataField tryGetDataField() {
        if (this.field instanceof IDataField) {
            return (IDataField) this.field;
        } else if (this.field instanceof IReferenceField) {
            return ((IReferenceField) this.field).getReferenceIdField();
        }

        return null;
    }

    protected String getDefaultInvalidInputValueMessage(String propertyDesc, Object inputValue) {
        return null;
    }

    protected final String getInvalidInputValueMessage(String propertyDesc, Object inputValue) {
        if (StringUtils.isEmpty(this.getInvalidInputValueMessagePattern())) {
            return this.getDefaultInvalidInputValueMessage(propertyDesc, inputValue);
        }

        return StringResources.resolve(this.getInvalidInputValueMessagePattern(), propertyDesc, inputValue);
    }

    protected final void setRawValue(Object dataObject, Object rawValue) {
        this.field.setValue(dataObject, rawValue);
    }

}
